
#include <iostream>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <cstring>
using namespace std;

const char* pathname = "./common.hpp";
const int proj_id = 0x66;

//将整形转换成16进制形式的字符串
string toHex(key_t k)
{
    char buff[1024];
    sprintf(buff, "0x%x", k);
    return buff;
}

//创建或获取共享内存
int creatOrGetshm(size_t size)
{
    //1、获取key
    //将pathname和proj_id转换成key，也可以不用ftok转换，自己指定。
    //但是通常采用函数，因为系统中shm可能很多，自己指定很容易造成重复
    key_t key = ftok(pathname, proj_id);
    if(key == -1)
        cout << "key is false, error message: " << strerror(errno) << endl;
    else
        cout << "key is true, key: " << toHex(key) << endl;

    //2、获取shm
    //IPC_CREAT 不存在就创建，存在就获取
    //IPC_EXCL 单独使用没有意义
    //IPC_CREAT | IPC_EXCL 不存在就创建，存在就报错
    //内核中共享内存的大小是以4K为基本单位的，你只能用你指定的大小空间，建议申请n*4K
    int shmid = shmget(key, size, IPC_CREAT | 0666);
    if(shmid == -1)
        cout << "shmid is false, error message: " << strerror(errno) << endl;
    else
        cout << "shmid is true, shmid: " << shmid << endl;
    return shmid;
}


//关联共享内存
void* shmAttach(int shmid)
{
    //将shm绑定到地址为nullptr的逻辑地址处，nullptr表示由操作系统决定
    //0表示shm中内容支持读写
    void* shmaddr = shmat(shmid, nullptr, 0);
    if((long long)shmaddr == -1)
        cout << "shmat is false, error message: " << strerror(errno) << endl;
    else
        cout << "shmat success, shmaddr: " << (long long)shmaddr << endl;
    return shmaddr;
}

//解除共享内存关联
void shmDetach(void* shmaddr)
{
    int n = shmdt(shmaddr);
    if(n == -1)
        cout << "shmdetach is false, error message: " << strerror(errno) << endl;
    else
        cout << "shmdetach success" << endl;
}


//删除共享内存
void deleteShm(int shmid)
{
    //当使用IPC_RMID时，表示将shm标记为删除，当所有关联都解除的时候，就删除，
    //所以和shm关联的进程，只要有一个进程调用删除就可以，因为shm的status被设置为dest，当nattach为0时，删除shm
    int n = shmctl(shmid, IPC_RMID, nullptr);
    if(n == -1)
        cout << "deleteshm fail, error message: " << strerror(errno) << endl;
    else
        cout << "deleteshm success" << endl;
}